package org.agony2d.gpu.supportClasses.programs {
	import com.adobe.utils.AGALMiniAssembler;
	
	import flash.display3D.Context3D;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.geom.Matrix3D;
	import flash.utils.Dictionary;
	
	import org.agony2d.core.agony_internal;
	import org.agony2d.gpu.AgonyAA;
	import org.agony2d.gpu.core.AAManager;
	import org.agony2d.gpu.core.GroupAA;
	import org.agony2d.gpu.core.NodeAA;
	import org.agony2d.gpu.core.VertexConvergence;
	import org.agony2d.gpu.supportClasses.textures.FullTexture;
	import org.agony2d.notify.AEvent;
	
	use namespace agony_internal;
	
	/** [ ProgramProxy ]
	 *  [★]
	 *  a.  ortho matrix :
	 *  		[A]
	 *  			[ base ]
	 *  				2.0 / width  ,   0             ,  0            , -(1 + x * 2.0 / width)
	 *  				0            , -(2.0 / height) ,  0            ,   1 + y * 2.0 / height
	 *  				0            ,   0             ,  1            ,   0
	 *  				0            ,   0             ,  0            ,   1
	 * 
	 *  			[ node ]
	 *  				1            ,   0             ,  0            ,   tx
	 *  				0            ,   1             ,  0            ,   ty
	 *  				0            ,   0             ,  1            ,   0
	 *  				0            ,   0             ,  0            ,   1
	 * 
	 *  		[B]
	 *  			x  =    (2.0 / width)  * tx - (1 + x * 2.0 / width)
	 *  			y  =  - (2.0 / height) * ty + (1 + y * 2.0 / height)
	 * 
	 *  		[C]
	 *  			1 + x  =  2.0 * (tx - x) / width
	 *  			1 - y  =  2.0 * (ty - y) / height
	 * 
	 *  		[D] range: (0 ~ 1)
	 *  			(1 + x) / 2.0  =  (tx - x) / width
	 *  			(1 - y) / 2.0  =  (ty - y) / height
	 *  		
	 */
public class ProgramProxy {
	
	public function ProgramProxy() {
		AgonyAA.addEventListener(AEvent.RESET, ____onReset)
		this.____onReset(null)
	}
	
	// overwrite...
	protected function get vertexCode() : String {
		return null
	}
	
	// overwrite...
	protected function get fragmentCode() :String {
		return null
	}
	
	agony_internal static function retrieve( tinted:Boolean, smoothing:Boolean, mipmapEnabled:Boolean ) : ProgramProxy {
		var proxy:ProgramProxy
		var type:uint
		
		if (!tinted && !smoothing) {
			type = A_UNCHANGED
		}
		else if (tinted && !smoothing) {
			type = A_TINTED
		}
		else {
			if (!tinted) {
				if (!mipmapEnabled) {
					type = A_SMOOTHING
				}
				else {
					type = A_SMOOTHING | A_MIPMAP
				}
			}
			else {
				if (!mipmapEnabled) {
					type = A_TINTED | A_SMOOTHING
				}
				else {
					type = A_TINTED | A_SMOOTHING | A_MIPMAP
				}
			}
		}
		proxy = g_programMap[type]
		if (!proxy) {
			proxy = g_programMap[type] = goRegisterProgramProxyByType(type)
		}
		return proxy
	}
	
	agony_internal static function goRegisterProgramProxyByType( type:uint ) : ProgramProxy {
		var proxy:ProgramProxy
		
		if (type == A_UNCHANGED) {
			proxy = new UnchangedProgram
		}
		else if (type == A_TINTED) {
			proxy = new TintedProgram
		}
		else if (type == A_SMOOTHING) {
			proxy = new SmoothingProgram
		}
		else if (type == (A_SMOOTHING | A_MIPMAP)) {
			proxy = new MipSmoothingProgram
		}
		else if (type == (A_TINTED | A_SMOOTHING)) {
			proxy = new TintedAndSmoothingProgram
		}
		else if (type == (A_TINTED | A_SMOOTHING | A_MIPMAP)) {
			proxy = new MipTintedAndSmoothingProgram
		}
		return proxy
	}
	
	agony_internal function uploadAllBuffers( group:GroupAA ) : void {
		var totalNodes:int
		
		g_currGroup = group
		totalNodes = group.totalNodes = g_convergence.m_totalNodes
		group.m_ib3d.uploadFromVector(g_convergence.m_indexList, 0, totalNodes * 6)
		group.m_vb3d.uploadFromByteArray(g_convergence.bytes, 0, 0, totalNodes * 4)	
	}
	
	agony_internal function setProperties( fullTexture:FullTexture ) : void {
		// set program3d...
		g_context3d.setProgram(m_rawProgram3d)
		
		// agal matrix3d const...
		g_context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, g_orthoMatrix3d, true)
		
		// agal vertex...
		g_context3d.setVertexBufferAt(0, g_currGroup.m_vb3d, 0, Context3DVertexBufferFormat.FLOAT_2)
		
		// agal uv...
		g_context3d.setVertexBufferAt(1, g_currGroup.m_vb3d, 2, Context3DVertexBufferFormat.FLOAT_2)
		
		// agal texture...
		g_context3d.setTextureAt(0, fullTexture.m_rawTexture)
	}
	
	agony_internal function drawAndReset() : void {
		g_context3d.drawTriangles(g_currGroup.m_ib3d, 0, g_currGroup.m_totalNodes * 2)
		g_convergence.reset()
		g_context3d.setTextureAt(0, null)
		g_context3d.setVertexBufferAt(0, null)
		g_context3d.setVertexBufferAt(1, null)
	}
	
	
	agony_internal static var A_UNCHANGED:uint  =  0x00
	agony_internal static var A_TINTED:uint     =  0x01
	agony_internal static var A_SMOOTHING:uint  =  0x02
	agony_internal static var A_MIPMAP:uint     =  0x04
	
	agony_internal static var g_context3d:Context3D
	agony_internal static var g_orthoMatrix3d:Matrix3D
	agony_internal static var g_programMap:Object = { }
	agony_internal static var g_convergence:VertexConvergence
	agony_internal static var g_currGroup:GroupAA
	
	agony_internal var m_rawProgram3d:Program3D
	
	
	agony_internal function ____onReset( e:AEvent ) : void {
		var assembler:AGALMiniAssembler
		
		assembler = new AGALMiniAssembler
		m_rawProgram3d = g_context3d.createProgram()
		m_rawProgram3d.upload(	assembler.assemble(Context3DProgramType.VERTEX,   this.vertexCode),
								assembler.assemble(Context3DProgramType.FRAGMENT, this.fragmentCode))
	}
	
}
}
